/*
 * Peripheral Simulator (PSim)
 *
 * Copyright (c) 2008 Avadh Patel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef PSIM_H
#define PSIM_H

#include "psim_lib.h"
#include <qemu-common.h>
#include <block.h>
//#include "vl.h"
#include <hw/hw.h>
#include <stdio.h>

#define SIMDEBUG
#ifdef SIMDEBUG
#define simdebug(...) fprintf(sim_fd, "Time:%f Function:%s :: ", sim_get_clock(), \
    		      __FUNCTION__);  \
    		      fprintf(sim_fd, __VA_ARGS__);
#include <assert.h>
#else
#define simdebug(...)
#define assert() 
#endif

//global variable simulate
extern uint8_t simulate;


extern FILE *sim_fd;

//following functions are implemented in qemu engine or monitor
void sim_term_print(const char*);

//enum DeviceType {
//  CPU,
//  PCI_BRIDGE,
//  PCI_BUS,
//  PCI_DEVICE,
//  NUM_TYPES
//};

//static char* DeviceTypeNames[NUM_TYPES] = {
//  "CPU",
//  "PCI_BRIDGE",
//  "PCI_BUS",
//  "PCI_DEVICE",
//};

//typedef uint64_t SimDeviceReadConfigFunc(void* dev, uint64_t addr, int size);
//typedef void SimDeviceWriteConfigFunc(void* dev, uint64_t addr, uint64_t data,
//    int size);

typedef Message* SimDeviceReadConfigFunc(Message* msg);
typedef void SimDeviceWriteConfigFunc(Message* msg);

//#ifndef SimDeviceIOFunc
//typedef void SimDeviceIOFunc(void* data);
typedef void SimDeviceIOFunc(Message* data);
//#endif

typedef void SimDeviceStopFunc(void);
typedef void SimDeviceInitFunc(void);

typedef struct Bind Bind;

typedef struct SimDevice SimDevice;

struct Bind
{
  SimDevice* dev;
  uint64_t addr_lo;
  uint64_t addr_hi;
};

struct SimDevice{
  enum DeviceType type;
  char name[100];
  //uint64_t addr;  //io addr of device
  void* dev; //pointer to the device structure
  uint8_t mmap; //memory map of device
  Bind* bindings; //Binding of device to other devices
  SimDeviceReadConfigFunc *read_config_cb;
  SimDeviceWriteConfigFunc *write_config_cb;
  SimDeviceIOFunc* in;
  SimDeviceStopFunc* stop_cb;
  SimDeviceInitFunc* init_cb;
  int id;

  //dynamic library file handler
  void *dl_handler;
};

//Simulation Init
void sim_init();
void sim_start();
void sim_stop();

//Simulator's clock functions
int64_t sim_get_cpu_clock();
int64_t sim_get_cpu_ticks();

//Find a device using a dev pointer
SimDevice* sim_find_dev_with_addr(uint64_t addr, int size); 
SimDevice* sim_get_dev_by_id(int id);

//register a device
//int sim_register_device(enum DeviceType type, const char* name, void* dev, uint64_t addr, int len, int size, uint8_t mmap) ;
SimDevice* sim_register_device(enum DeviceType type, const char* name, void* dl, uint8_t mmap) ;

void sim_register_addr(SimDevice* dev, uint64_t addr, int len, int size);
void sim_register_qemu_device(SimDevice* dev, void* p_dev);

//register callback functions
void sim_read_config_cb(SimDevice* dev, SimDeviceReadConfigFunc *_cb) ;

void sim_write_config_cb(SimDevice* dev, SimDeviceWriteConfigFunc *_cb) ;

void sim_register_in_cb(SimDevice* dev, SimDeviceIOFunc *_cb);
void sim_register_out_cb(SimDevice* dev, SimDeviceIOFunc *_cb);

void sim_register_simulation_stop_cb(SimDevice* dev, SimDeviceStopFunc *_cb);

int simulate_io_write(uint64_t addr, uint64_t data, int size, void* opaque) ;

int simulate_io_read(uint64_t addr, uint64_t *data, int size, void* opaque) ;

//Get bridge function
void sim_init_bridge();
SimDevice* sim_get_bridge();
SimDevice* sim_get_cpu();
SimDevice* sim_get_cache();
//SMM support
typedef void SimDeviceSMMFunc(int val);
void sim_register_smm_cb(SimDeviceSMMFunc* _smm_cb);
void sim_set_smm(int val);
//Memory mappings
typedef void SimDeviceMemInitFunc(void);
void sim_register_init_mem_map_cb(SimDeviceMemInitFunc* _mmap_cb);
void sim_init_mmap();

//Get/Set PIC functions
void sim_set_pic(qemu_irq* _pic);
qemu_irq* sim_get_pic();

//IDE functions
void sim_set_ide_devfn(int fn);
int sim_get_ide_devfn();
void sim_set_hd_table(BlockDriverState** hd);
BlockDriverState** sim_get_hd_table();
void sim_init_ide();

//Network card functions
void sim_init_network_card();
void sim_set_nicinfo(NICInfo* nd);
NICInfo* sim_get_nicinfo();

void sim_set_pci_bridge_dev(PCIDevice* dev);
PCIDevice* sim_get_pci_bridge_dev();
void sim_set_pci_bus(PCIBus* bus);
PCIBus* sim_get_pci_bus();
void sim_set_isa_bridge_dev(PCIDevice* dev);
PCIDevice* sim_get_isa_bridge_dev();

//cache access
void sim_cache_access(unsigned long addr, int data_size, uint8_t type);

//Simulation functions
void sim_vm_stop();

//Wrapper functions
uint32_t sim_get_physical_page_desc(target_phys_addr_t addr);

//Stats related functions
struct stat_sdb_t* sim_get_stat_db();
void sim_dump_stats1(const char* filename);
void sim_clear_stats();

//Block driver Wrapper functions
BlockDriverAIOCB *sim_bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
                                 const uint8_t *buf, int nb_sectors,
                                 BlockDriverCompletionFunc *cb, void *opaque);
BlockDriverAIOCB *sim_bdrv_aio_read(BlockDriverState *bs, int64_t sector_num,
                                 const uint8_t *buf, int nb_sectors,
                                 BlockDriverCompletionFunc *cb, void *opaque);
void sim_bdrv_aio_cancel(BlockDriverAIOCB *acb);
void sim_bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
void sim_bdrv_set_change_cb(BlockDriverState *bs,
                        void (*change_cb)(void *opaque), void *opaque);
void sim_bdrv_eject(BlockDriverState *bs, int eject_flag);
int sim_cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
int sim_cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
int sim_bdrv_get_type_hint(BlockDriverState *bs);
void sim_bdrv_set_locked(BlockDriverState *bs, int locked);
void sim_bdrv_flush(BlockDriverState *bs);

IOPortReadFunc* get_ioport_read_func(uint32_t addr, uint8_t size);
IOPortWriteFunc* get_ioport_write_func(uint32_t addr, uint8_t size);

#endif //PSIM_H
